/*
 Copyright (c) [Year] [name of copyright holder]
 [Software Name] is licensed under Mulan PSL v2.
 You can use this software according to the terms and conditions of the Mulan
 PSL v2.
 You may obtain a copy of Mulan PSL v2 at:
          http://license.coscl.org.cn/MulanPSL2
 THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
 KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 See the Mulan PSL v2 for more details.
*/

#include "PasteConvert.hpp"

/**
 * @brief 将一个字符串转换为两个整数
 *
 * 该函数接受一个字符串作为输入，并将其转换为两个整数num1和num2
 * 字符串应该包含两个由 "*" 或 "x" 或 "X" 字符分隔的整数
 *
 * @param str 输入的字符串，格式应为 "数字*数字" 或 "数字x数字" 或 "数字X数字"
 * @param num1Out 引用参数，将转换后的第一个整数存储于此
 * @param num2Out 引用参数，将转换后的第二个整数存储于此
 */
void PasteConvert::StrTo2Num(const std::string &str, int &num1Out, int &num2Out) {
    // 检查输入字符串是否为空，如果为空则抛出异常
    if (str.empty()) {
        throw std::runtime_error("[PasteConvert::StrTo2Num] 输入字符串为空");
    }

    // 定义分隔符，用于分割字符串
    const std::string separators = "*xX";
    std::size_t pos = std::string::npos;

    // 查找字符串中的分隔符
    for (char sep : separators) {
        pos = str.find(sep);
        if (pos != std::string::npos) {
            break;
        }
    }

    // 如果没有找到分隔符，则抛出异常
    if (pos == std::string::npos) {
        throw std::runtime_error("[PasteConvert::StrTo2Num] 字符串中找不到分隔符");
    }

    // 分割字符串为两部分
    std::string part1 = str.substr(0, pos);
    std::string part2 = str.substr(pos + 1);

    // 检查分割后的字符串是否为空，如果为空则抛出异常
    if (part1.empty() || part2.empty()) {
        throw std::runtime_error("[PasteConvert::StrTo2Num] 字符串分裂后有空的子字符串");
    }

    // 将分割后的字符串转换为整数
    try {
        num1Out = std::stoi(part1);
        num2Out = std::stoi(part2);
    } catch (const std::invalid_argument &e) {
        // 如果转换失败，则抛出异常
        throw std::runtime_error(std::string("[PasteConvert::StrTo2Num] 转换为整数失败: ") + e.what());
    } catch (const std::out_of_range &e) {
        // 如果转换结果超出整数范围，则抛出异常
        throw std::runtime_error(std::string("[PasteConvert::StrTo2Num] 转换为整数失败: 数值超出范围: ") + e.what());
    }
}

// 生成一个唯一的GUID字符串
void PasteConvert::GenerateUUID(std::string &uuid) {
    // 初始化随机数设备和随机数生成器，保证线程安全
    thread_local static std::random_device rd;
    thread_local static std::mt19937 gen(rd());
    // 初始化两个均匀分布的随机数生成器，用于生成GUID的不同部分
    thread_local static std::uniform_int_distribution<> dis(0, 15);
    thread_local static std::uniform_int_distribution<> dis2(8, 11);

    // 使用stringstream来构建GUID字符串
    std::stringstream ss;
    int i;
    // 设置流为十六进制输出模式
    ss << std::hex;
    // 生成GUID的第一部分（8个十六进制数字）
    for (i = 0; i < 8; i++) {
        ss << dis(gen);
    }
    // 添加GUID格式的连字符
    ss << "-";
    // 生成GUID的第二部分（4个十六进制数字）
    for (i = 0; i < 4; i++) {
        ss << dis(gen);
    }
    // 添加GUID格式的连字符和第三部分的第一个数字（范围8-11）
    ss << "-";
    ss << dis2(gen);
    // 生成第三部分剩余的3个十六进制数字
    for (i = 0; i < 3; i++) {
        ss << dis(gen);
    }
    // 添加GUID格式的连字符和第四部分的第一个数字（范围8-11）
    ss << "-";
    ss << dis2(gen);
    // 生成第四部分剩余的3个十六进制数字
    for (i = 0; i < 3; i++) {
        ss << dis(gen);
    }
    // 添加GUID格式的连字符和最后一部分（12个十六进制数字）
    ss << "-";
    for (i = 0; i < 12; i++) {
        ss << dis(gen);
    };
    // 返回生成的GUID字符串
    uuid = ss.str();
}

/**
 * @brief 从给定的字符串中提取尺寸和文本信息。
 *
 * 该函数使用正则表达式来解析输入字符串，旨在提取两个尺寸信息（以毫米为单位）和额外的文本信息。
 * 如果输入字符串符合预期的格式，函数将成功提取信息并返回0；否则，返回1表示失败。
 *
 * @param input 输入字符串，包含尺寸和文本信息。
 * @param dim1 第一个尺寸信息，以毫米为单位。
 * @param dim2 第二个尺寸信息，以毫米为单位。如果没有提供第二个尺寸，则默认为0。
 * @param text 提取的文本信息。
 * @return 0 表示成功提取信息，1 表示提取信息失败。
 */
int PasteConvert::ExtDimensions(const std::string &input, double &dim1, double &dim2, std::string &text) {
    // 定义正则表达式模式，用于匹配和提取尺寸和文本信息。
    std::regex pattern(R"(^(\D*)(\d+(\.\d*)?)mm(\s*\*\s*(\d+(\.\d*)?)mm)?(.*)$)");
    std::smatch match;

    try {
        // 使用正则表达式进行匹配，如果匹配成功且捕获组数量足够，则进行信息提取。
        if (std::regex_search(input, match, pattern) && match.size() >= 8) {
            // 组合第一个和最后一个捕获组的内容，作为提取的文本信息。
            text = match[1].str() + match[7].str();
            // 将捕获的尺寸信息转换为double类型。
            dim1 = std::stod(match[2].str());
            // 检查是否提供了第二个尺寸信息，如果提供了，则进行转换；否则，默认为0。
            if (match[5].matched) {
                dim2 = std::stod(match[5].str());
            } else {
                dim2 = 0;
            }
            // 成功提取信息，返回0。
            return 0;
        } else {
            return 1; // 如果匹配失败或捕获组数量不足，表示输入格式不正确，返回1。
        }
    } catch (const std::invalid_argument &e) {
        return 1; // 捕获无效参数异常，表示转换过程中出现问题，返回1。
    } catch (const std::out_of_range &e) {
        return 1; // 捕获超出范围异常，表示转换过程中出现问题，返回1。
    }
}

/**
 * @brief 在目标字符串中添加源字符串
 *
 * 此函数旨在将源字符串（src）添加到目标字符串（dest）中，
 * 具体位置由isLeft参数决定, 
 * 如果源字符串为空，则不进行任何操作,
 * 如果目标字符串中已包含源字符串，则不会重复添加
 *
 * @param dest 目标字符串，将被源字符串添加到该字符串中
 * @param src 源字符串，要添加到目标字符串中的字符串
 * @param isLeft 布尔值，决定源字符串是添加在目标字符串的左侧（true）还是右侧（false）
 */
void PasteConvert::AddString(std::string &dest, const std::string &src, bool isLeft) {
    if (src.empty()) {
        return; // 空字符串不做处理
    }
    size_t pos = dest.find(src);
    if (pos == std::string::npos) {
        if (isLeft) {
            dest = src + dest;
        } else {
            dest += src;
        }
    }
}